package wepay

import (
	"crypto/tls"
	"encoding/xml"
	"errors"
	"fmt"
	"net/url"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/httplib"
	"github.com/astaxie/beego/logs"
)

const (
	createPaymentUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"
	queryPamentUrl   = "https://api.mch.weixin.qq.com/pay/orderquery"
	scencInfo        = `{"h5_info":{"type":"Wap","wap_url":"https://www.topchina86.com","wap_name":"流量平台"}}`
)

type WePay struct {
	appid    string
	mchId    string //商户号
	key      string
	callBack string //支付消息回调
	redirect string //支付完成后跳转页面
}

func NewWepay(appid, mchId, key, callback, redirect string) *WePay {

	return &WePay{
		appid:    appid,
		mchId:    mchId,
		key:      key,
		callBack: callback,
		redirect: redirect,
	}
}

type cdataString struct {
	Value string `xml:",cdata"`
}

type payment struct {
	XMLName          xml.Name    `xml:"xml"`
	Appid            string      `xml:"appid"`
	Mch_id           string      `xml:"mch_id"` //微信支付分配的商户号
	Device_info      string      `xml:"device_info"`
	Nonce_str        string      `xml:"nonce_str"`
	Sign             string      `xml:"sign"`
	Sign_type        string      `xml:"sign_type"`
	Body             string      `xml:"body"`
	Detail           string      `xml:"detail"`
	Attach           string      `xml:"attach"`
	Out_trade_no     string      `xml:"out_trade_no"`
	Fee_type         string      `xml:"fee_type"`
	Total_fee        int32       `xml:"total_fee"`
	Spbill_create_ip string      `xml:"spbill_create_ip"`
	Time_start       string      `xml:"time_start"`  //yyyyMMddHHmmss
	Time_expire      string      `xml:"time_expire"` //20091227091010
	Goods_tag        string      `xml:"goods_tag"`   //订单优惠标记，使用代金券或立减优惠功能时需要的参数，说明详见代金券或立减优惠
	Notify_url       string      `xml:"notify_url"`  //异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。
	Trade_type       string      `xml:"trade_type"`  //JSAPI--JSAPI支付（或小程序支付）、NATIVE--Native支付、APP--app支付，MWEB--H5支付
	Product_id       string      `xml:"product_id"`  //12235413214070356458058
	Limit_pay        string      `xml:"limit_pay"`   //上传此参数no_credit--可限制用户不能使用信用卡支付
	Scence           cdataString `xml:"scene_info"`  //支付场景
	OpenId           string      `xml:"openid"`
}

/*
<xml><return_code><![CDATA[FAIL]]></return_code>
<return_msg><![CDATA[商户号该产品权限预开通中，请等待产品开通后重试]]></return_msg>
</xml>
*/

type WepayReturn struct {
	RetCode  string `xml:"return_code"`
	RetMsg   string `xml:"return_msg"`
	MwebUrl  string `xml:"mweb_url"`
	ErrCode  string `xml:"err_code"`
	PrepayId string `xml:"prepay_id"`
}

func (p WePay) CreateNewPay(userIp, tradeId, product, payType, detail string, cost int32) (string, string, error) {

	request := payment{

		Total_fee:        cost,
		Appid:            p.appid,
		Mch_id:           p.mchId,
		Nonce_str:        getRandomString(32),
		Body:             detail,
		Out_trade_no:     tradeId,
		Spbill_create_ip: userIp,
		Time_start:       time.Now().Format("20060102150405"),
		Time_expire:      time.Now().Add(1 * time.Hour).Format("20060102150405"),
		Notify_url:       p.callBack,
		Trade_type:       payType,
		Sign_type:        "HMAC-SHA256",
		Attach:           userIp + "_" + tradeId,
		Scence:           cdataString{scencInfo},
		//Detail:           product,
		//Fee_type:         "CNY",
		//Limit_pay:        "",
		//Goods_tag:        "",
	}

	sign := newParamList()

	sign.addParam("appid", request.Appid)
	sign.addParam("attach", request.Attach)
	sign.addParam("body", request.Body)
	sign.addParam("mch_id", request.Mch_id)
	sign.addParam("nonce_str", request.Nonce_str)
	sign.addParam("out_trade_no", request.Out_trade_no)
	sign.addParam("sign_type", request.Sign_type)
	sign.addParam("spbill_create_ip", request.Spbill_create_ip)
	sign.addParam("time_expire", request.Time_expire)
	sign.addParam("time_start", request.Time_start)
	sign.addParam("total_fee", fmt.Sprintf("%d", request.Total_fee))
	sign.addParam("trade_type", request.Trade_type)
	sign.addParam("scene_info", request.Scence.Value)
	sign.addParam("detail", request.Detail)
	sign.addParam("device_info", request.Device_info)
	sign.addParam("fee_type", request.Fee_type)
	sign.addParam("goods_tag", request.Goods_tag)
	sign.addParam("limit_pay", request.Limit_pay)
	sign.addParam("product_id", request.Product_id)
	sign.addParam("openid", request.OpenId)
	sign.addParam("notify_url", request.Notify_url)

	request.Sign = wechatSign(sign, p.key)
	logs.Debug("sign:%v", request.Sign)

	requstBody, err := xml.Marshal(request)
	if err != nil {
		logs.Error("xml encode failed:", err)
		return "", "", errors.New("系统错误，请重试")
	}

	req := httplib.Post(createPaymentUrl)
	req.Body(requstBody).SetTimeout(5*time.Second, 5*time.Second)
	req.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})

	response, err := req.Bytes()
	if err != nil {
		beego.Error("request to wx failed")
		return "", "", errors.New("支付失败，请重试")
	}

	wepayResult := &WepayReturn{}
	err = xml.Unmarshal(response, wepayResult)
	if err != nil {
		beego.Error("request to wx failed:%v", string(response))
		return "", "", errors.New("wepay return error")
	}

	if wepayResult.RetCode != "SUCCESS" {
		beego.Error("request to wx failed:%v", string(response))
		return "", "", errors.New(wepayResult.RetMsg)
	}

	if wepayResult.RetMsg != "OK" {
		beego.Error("request to wx failed:%v", string(response))
		return "", "", errors.New(wepayResult.ErrCode)
	}

	logs.Debug("recv wepay response:%V", string(response))

	wepayUrl := wepayResult.MwebUrl
	if len(p.redirect) > 0 {
		v := url.Values{}

		v.Add("redirect_url", p.redirect)
		redirect := v.Encode()
		wepayUrl += "&" + redirect
	}

	return wepayUrl, wepayResult.PrepayId, nil
}

type queryPaymentParam struct {
	XMLName      xml.Name `xml:"xml"`
	Appid        string   `xml:"appid"`
	Mch_id       string   `xml:"mch_id"` //微信支付分配的商户号
	Nonce_str    string   `xml:"nonce_str"`
	Out_trade_no string   `xml:"out_trade_no"`
	Sign         string   `xml:"sign"`
	Sign_type    string   `xml:"sign_type"`
}

type WepayBillStatusReturn struct {
	RetCode     string `xml:"return_code"`
	RetMsg      string `xml:"return_msg"`
	ResultCode  string `xml:"result_code"`
	ErrCode     string `xml:"err_code"`
	PrepayId    string `xml:"prepay_id"`
	TradeStatus string `xml:"trade_state"`
	TradeId     string `xml:"out_trade_no"`
	Price       int    `xml:"total_fee"`
	WepayId     string `xml:"transaction_id"`
	Subscribe   string `xml:"is_subscribe"`
}

func (p WePay) QueryPayment(tradeId string) {

	request := queryPaymentParam{
		Appid:        p.appid,
		Mch_id:       p.mchId,
		Nonce_str:    getRandomString(32),
		Out_trade_no: tradeId,
	}

	sign := newParamList()
	sign.addParam("appid", request.Appid)
	sign.addParam("mch_id", request.Mch_id)
	sign.addParam("nonce_str", request.Nonce_str)
	sign.addParam("out_trade_no", request.Out_trade_no)
	sign.addParam("trade_type", request.Sign_type)

	request.Sign = wechatSign(sign, p.key)

	requstBody, err := xml.Marshal(request)
	if err != nil {
		logs.Error("xml encode failed:", err)
		return
	}

	req := httplib.Post(queryPamentUrl)
	req.Body(requstBody).SetTimeout(5*time.Second, 5*time.Second)
	req.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})

	response, err := req.Bytes()
	if err != nil {
		beego.Error("request to wx failed")
		return
	}

	logs.Debug("recv:%V", string(response))
	return

}
